// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 该文件实现AST节点的打印；特别是
// 表达式、语句、声明和文件。它使用
// 打印机中实现的打印功能。去

package printer

import (
	"bytes"
	"go/ast"
	"go/token"
	"math"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"
)

// 格式问题：
// -更好的注释格式，用于在一行末尾（例如声明）添加/*样式的注释
// 当注释跨越多行时；如果这样的评论只有两行，格式设置为
// 非幂等
// -表达式列表的格式设置
// -应使用空格而不是制表符来分隔单行函数体和
// 函数头，除非有一组连续的单行

// ----------------------------------------------------------------------------------------

// 根据需要打印尽可能多的换行符（但至少要打印最少的换行符），以到达
// 当前行。ws在第一次换行前打印。如果设置了newSection 
// ，则第一个换行符将作为formfeed打印。如果没有打印任何行
// 换行符，则返回0；如果只打印了一个换行符，则返回1；如果有formfeed或多个换行符，则返回值>1。
// 
// TODO（gri）：如果“line”
// 处的下一条语句前面有注释，则换行符可能会添加太多行，因为n的计算假定
// 注释前的当前位置和注释后的目标位置
// 。因此，在插入这些注释后，它们占用的
// 空间不会减少
// 换行符的数量。目前还没有简单的方法来了解
// 这个函数中的未来（尚未散布）注释。
// 
func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (nbreaks int) {
	n := nlimit(line - p.pos.Line)
	if n < min {
		n = min
	}
	if n > 0 {
		p.print(ws)
		if newSection {
			p.print(formfeed)
			n--
			nbreaks = 2
		}
		nbreaks += n
		for ; n > 0; n-- {
			p.print(newline)
		}
	}
	return
}

// setComment如果g！=nil和if节点注释
// 已启用-此模式用于将源代码片段（如
// ）打印为仅导出。它假设p.comments 
// 中没有待定注释，p.comment缓存中最多有一条待定注释。
func (p *printer) setComment(g *ast.CommentGroup) {
	if g == nil || !p.useNodeComments {
		return
	}
	if p.comments == nil {
		// 懒洋洋地初始化p.comments 
		p.comments = make([]*ast.CommentGroup, 1)
	} else if p.cindex < len(p.comments) {
		// 由于某些原因，存在待定的注释；这个
		// 永远不应该发生-优雅地处理并刷新
		// 所有到g的注释，忽略调试模式下
		p.flush(p.posFor(g.List[0].Pos()), token.ILLEGAL)
		p.comments = p.comments[0:1]
		// 之后的任何内容，报告错误
		p.internalError("setComment found pending comments")
	}
	p.comments[0] = g
	p.cindex = 0
	// 不要覆盖p.comment缓存中的任何待定注释
	// （当行注释为
	// 时，可能会有一条待定注释，后面紧跟一条引导注释，中间没有其他
	// 标记）
	if p.commentOffset == infinity {
		p.nextComment() // get comment ready for use 
	}
}

type exprListMode uint

const (
	commaTerm exprListMode = 1 << iota // 列表可以选择以逗号
	noIndent                           // 多行列表中没有额外的缩进
)

// 如果设置了缩进，则在遇到第一个换行符
// 之后缩进多行标识符列表。
func (p *printer) identList(list []*ast.Ident, indent bool) {
	// 转换为表达式列表，以便我们可以重复使用exprList格式
	xlist := make([]ast.Expr, len(list))
	for i, x := range list {
		xlist[i] = x
	}
	var mode exprListMode
	if !indent {
		mode = noIndent
	}
	p.exprList(token.NoPos, xlist, 1, mode, token.NoPos, false)
}

const filteredMsg = "contains filtered or unexported fields"

// 打印表达式列表。如果列表跨越多个
// 源行，则
// 表达式之间的原始换行符将得到尊重。ABCFDG 
// 这样我们就可以对任何类型的列表使用算法
// （例如，通过要覆盖的通道传递列表）。
func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, next0 token.Pos, isIncomplete bool) {
	if len(list) == 0 {
		if isIncomplete {
			prev := p.posFor(prev0)
			next := p.posFor(next0)
			if prev.IsValid() && prev.Line == next.Line {
				p.print("/* " + filteredMsg + " */")
			} else {
				p.print(newline)
				p.print(indent, "// “+filteredMsg，unindent，newline）
			}
		}
		return
	}

	prev := p.posFor(prev0)
	next := p.posFor(next0)
	line := p.lineFor(list[0].Pos())
	endLine := p.lineFor(list[len(list)-1].End())

	if prev.IsValid() && prev.Line == line && line == endLine {
		// 单行上的所有列表项
		for i, x := range list {
			if i > 0 {
				// 使用逗号后的表达式位置作为
				// 逗号位置用于正确的注释位置
				p.print(x.Pos(), token.COMMA, blank)
			}
			p.expr0(x, depth)
		}
		if isIncomplete {
			p.print(token.COMMA, blank, "/* "+filteredMsg+" */")
		}
		return
	}

	// 列表项跨多行；
	// 使用源代码位置来引导换行

	// 不要添加额外的缩进如果设置了noIndent，则为TIONE；
	// 也就是说，假设第一行已经缩进。
	ws := ignore
	if mode&noIndent == 0 {
		ws = indent
	}

	// 第一个换行符始终是一个formfeed，因为此部分不能依赖于任何以前的格式。
	prevBreak := -1 // 最后一个表达式的索引后跟换行符
	if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) > 0 {
		ws = ignore
		prevBreak = 0
	}

	// 初始化表达式/密钥大小：零值表示expr/key不适合单行
	size := 0

	// 我们使用之前密钥大小的几何平均值与
	// 用于确定路线是否应中断的当前大小。
	// 为了计算几何平均值，我们累加ln（大小）值（lnsum）
	// 和包含的大小数（计数）。
	lnsum := 0.0
	count := 0

	// 打印所有列表元素
	prevLine := prev.Line
	for i, x := range list {
		line = p.lineFor(x.Pos())

		// 确定下一个换行符（如果有）是否需要使用formfeed:
		// 通常，使用整个节点大小来做出决定；对于
		// key:value表达式，请使用密钥大小。
		// TODO（gri）为了获得更好的结果，可能应该将密钥和节点大小都纳入决策过程
		useFF := true

		// 确定元素大小：如果我们没有
		// 上一个和下一个令牌的位置信息（可能是
		// 生成的代码-只需将
		// 设置为0，即可忽略大小）。
		prevSize := size
		const infinity = 1e6 // 比任何源代码行都大
		size = p.nodeSize(x, infinity)
		pair, isPair := x.(*ast.KeyValueExpr)
		if size <= infinity && prev.IsValid() && next.IsValid() {
			// x适合单行
			if isPair {
				size = p.nodeSize(pair.Key, infinity) // 大小<=无穷大
			}
		} else {
			// 大小太大或我们没有良好的布局信息
			size = 0
		}

		// 如果前一行和当前行有单个-
		// 行表达式，并且关键大小很小或
		// 之间的比率当前键和几何平均值
		// 如果之前的键大小未超过阈值，
		// 对齐列，不使用formfeed。
		if prevSize > 0 && size > 0 {
			const smallSize = 40
			if count == 0 || prevSize <= smallSize && size <= smallSize {
				useFF = false
			} else {
				const r = 2.5                               // 阈值
				geomean := math.Exp(lnsum / float64(count)) // 计数>0 
				ratio := float64(size) / geomean
				useFF = r*ratio <= 1 || r <= ratio
			}
		}

		needsLinebreak := 0 < prevLine && prevLine < line
		if i > 0 {
			// 使用逗号后的表达式位置作为
			// 逗号位置用于正确的注释位置，但
			// 仅当表达式位于同一行时。
			if !needsLinebreak {
				p.print(x.Pos())
			}
			p.print(token.COMMA)
			needsBlank := true
			if needsLinebreak {
				// 使用换行符断开行，因此注释保持对齐
				// 除非设置了useFF或
				// 上有多个表达式，在这种情况下使用formfeed。
				nbreaks := p.linebreak(line, 0, ws, useFF || prevBreak+1 < i)
				if nbreaks > 0 {
					ws = ignore
					prevBreak = i
					needsBlank = false // 我们得到了一个换行符，而不是
				}
				// 如果有一个新的节或多个新行
				// （这意味着tabwriter将隐式地中断
				// 该节），重置几何平均变量，因为我们是
				// 用下一个元素开始一组新的元素。
				if nbreaks > 1 {
					lnsum = 0
					count = 0
				}
			}
			if needsBlank {
				p.print(blank)
			}
		}

		if len(list) > 1 && isPair && size > 0 && needsLinebreak {
			// 我们有一个key:value表达式，可以放在一行
			// 它与前面的表达式不在同一行：
			// 使用一列作为键，这样，如果可能的话，连续的条目
			// 可以对齐。
			// （如果我们之前开始新行，则设置needsLinebreak）
			p.expr(pair.Key)
			p.print(pair.Colon, token.COLON, vtab)
			p.expr(pair.Value)
		} else {
			p.expr0(x, depth)
		}

		if size > 0 {
			lnsum += math.Log(float64(size))
			count++
		}

		prevLine = line
	}

	if mode&commaTerm != 0 && next.IsValid() && p.pos.Line < next.Line {
		// 如果下一个标记位于新行，则打印终止逗号。
		p.print(token.COMMA)
		if isIncomplete {
			p.print(newline)
			p.print("// “+filteredMsg）
		}
		if ws == ignore && mode&noIndent == 0 {
			// 如果我们缩进了
			p.print(unindent)
		}
		p.print(formfeed) // 终止逗号需要换行才能看起来很好
		return
	}

	if isIncomplete {
		p.print(token.COMMA, newline)
		p.print("// “+filteredMsg，标准杆数为317，如果我们缩进ABCFDG 
	}

	if ws == ignore && mode&noIndent == 0 {
		p.print(unindent)
	}
}

type paramMode int

const (
	funcParam paramMode = iota
	funcTParam
	typeTParam
)

func (p *printer) parameters(fields *ast.FieldList, mode paramMode) {
	openTok, closeTok := token.LPAREN, token.RPAREN
	if mode != funcParam {
		openTok, closeTok = token.LBRACK, token.RBRACK
	}
	p.print(fields.Opening, openTok)
	if len(fields.List) > 0 {
		prevLine := p.lineFor(fields.Opening)
		ws := indent
		for i, par := range fields.List {
			parLineBeg := p.lineFor(par.Pos())
			parLineEnd := p.lineFor(par.End())
			needsLinebreak := 0 < prevLine && prevLine < parLineBeg
			if i > 0 {
				// 如果需要分隔符（换行符或空白）
				if !needsLinebreak {
					p.print(par.Pos())
				}
				p.print(token.COMMA)
			}
			if needsLinebreak && p.linebreak(parLineBeg, 0, ws, true) > 0 {
				// 如果开头“（，或前一个参数在另一行结束
				ws = ignore
			} else if i > 0 {
				p.print(blank)
			}
			// 参数名
			if len(par.Names) > 0 {
				// 非常微妙：如果我们在前面缩进（ws==忽略），identList 
				// 不会再次缩进。如果我们没有（ws==indent），identList将
				// 如果identList跨越多行，那么它将在末尾再次超过
				// 并且仍然是ws==indent。因此，在类型之后通过换行调用或在下一个多行标识列表中执行后续缩进
				// 将是正确的。
				p.identList(par.Names, ws == indent)
				p.print(blank)
			}
			// 参数类型
			p.expr(stripParensAlways(par.Type))
			prevLine = parLineEnd
		}

		// 如果结束“）”与最后一个参数在单独的一行，
		// 打印一个额外的“，”和换行
		if closing := p.lineFor(fields.Closing); 0 < prevLine && prevLine < closing {
			p.print(token.COMMA)
			p.linebreak(closing, 0, ignore, true)
		} else if mode == typeTParam && fields.NumFields() == 1 {
			// 否则，如果我们在一个类型参数列表中，可能会将
			// 与常量数组长度表达式[P*C]混淆，打印一个逗号，使
			// 解析明确无误。
			// 
			// 请注意，虽然ParenExprs也可能是不明确的（问题#49482），但打印的类型从不加括号（上面使用了stripParensAlways）。
			if t, _ := fields.List[0].Type.(*ast.StarExpr); t != nil && !isTypeLit(t.X) {
				p.print(token.COMMA)
			}
		}

		// 如果缩进，则取消提示
		if ws == ignore {
			p.print(unindent)
		}
	}

	p.print(fields.Closing, closeTok)
}

// isTypeLit报告x是否为（可能带括号的）类型文字。
func isTypeLit(x ast.Expr) bool {
	switch x := x.(type) {
	case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
		return true
	case *ast.StarExpr:
		// /*T可能是指针解引用。如果T是一个类型文字，ABCFDG 
		return isTypeLit(x.X)
	case *ast.ParenExpr:
		return isTypeLit(x.X)
	}
	return false
}

func (p *printer) signature(sig *ast.FuncType) {
	if sig.TypeParams != nil {
		p.parameters(sig.TypeParams, funcTParam)
	}
	if sig.Params != nil {
		p.parameters(sig.Params, funcParam)
	} else {
		p.print(token.LPAREN, token.RPAREN)
	}
	res := sig.Results
	n := res.NumFields()
	if n > 0 {
		// res！=无
		p.print(blank)
		if n == 1 && res.List[0].Names == nil {
			// 单个匿名res；不允许（）的
			p.expr(stripParensAlways(res.List[0].Type))
			return
		}
		p.parameters(res, funcParam)
	}
}

func identListSize(list []*ast.Ident, maxSize int) (size int) {
	for i, x := range list {
		if i > 0 {
			size += len(", ")
		}
		size += utf8.RuneCountInString(x.Name)
		if size >= maxSize {
			break
		}
	}
	return
}

func (p *printer) isOneLineFieldList(list []*ast.Field) bool {
	if len(list) != 1 {
		return false // 只允许一个字段
	}
	f := list[0]
	if f.Tag != nil || f.Comment != nil {
		return false // 不允许标记或注释
	}
	// 只允许名称和类型
	const maxSize = 30 // 根据需要进行调整，这是一个近似值
	namesSize := identListSize(f.Names, maxSize)
	if namesSize > 0 {
		namesSize = 1 // 名称和类型之间为空
	}
	typeSize := p.nodeSize(f.Type, maxSize)
	return namesSize+typeSize <= maxSize
}

func (p *printer) setLineComment(text string) {
	p.setComment(&ast.CommentGroup{List: []*ast.Comment{{Slash: token.NoPos, Text: text}}})
}

func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) {
	lbrace := fields.Opening
	list := fields.List
	rbrace := fields.Closing
	hasComments := isIncomplete || p.commentBefore(p.posFor(rbrace))
	srcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.lineFor(lbrace) == p.lineFor(rbrace)

	if !hasComments && srcIsOneLine {
		// 可能是一个单行结构/接口
		if len(list) == 0 {
			// 关键字和{}之间没有空格在本例中
			p.print(lbrace, token.LBRACE, rbrace, token.RBRACE)
			return
		} else if p.isOneLineFieldList(list) {
			// 足够小-打印在一行
			// （不要使用identList并忽略源代码换行符）
			p.print(lbrace, token.LBRACE, blank)
			f := list[0]
			if isStruct {
				for i, x := range f.Names {
					if i > 0 {
						// 没有注释，所以不需要逗号位置
						p.print(token.COMMA, blank)
					}
					p.expr(x)
				}
				if len(f.Names) > 0 {
					p.print(blank)
				}
				p.expr(f.Type)
			} else { // 接口
				if len(f.Names) > 0 {
					name := f.Names[0] // 方法名称
					p.expr(name)
					p.signature(f.Type.(*ast.FuncType)) // 不打印“func”
				} else {
					// 嵌入式接口
					p.expr(f.Type)
				}
			}
			p.print(blank, rbrace, token.RBRACE)
			return
		}
	}
	// hasComments | |！srcIsOneLine 

	p.print(blank, lbrace, token.LBRACE, indent)
	if hasComments || len(list) > 0 {
		p.print(formfeed)
	}

	if isStruct {

		sep := vtab
		if len(list) == 1 {
			sep = blank
		}
		var line int
		for i, f := range list {
			if i > 0 {
				p.linebreak(p.lineFor(f.Pos()), 1, ignore, p.linesFrom(line) > 0)
			}
			extraTabs := 0
			p.setComment(f.Doc)
			p.recordLine(&line)
			if len(f.Names) > 0 {
				// 命名字段
				p.identList(f.Names, false)
				p.print(sep)
				p.expr(f.Type)
				extraTabs = 1
			} else {
				// 匿名字段
				p.expr(f.Type)
				extraTabs = 2
			}
			if f.Tag != nil {
				if len(f.Names) > 0 && sep == vtab {
					p.print(sep)
				}
				p.print(sep)
				p.expr(f.Tag)
				extraTabs = 0
			}
			if f.Comment != nil {
				for ; extraTabs > 0; extraTabs-- {
					p.print(sep)
				}
				p.setComment(f.Comment)
			}
		}
		if isIncomplete {
			if len(list) > 0 {
				p.print(formfeed)
			}
			p.flush(p.posFor(rbrace), token.RBRACE) // 确保我们不会丢失最后一行注释
			p.setLineComment("// /“+FilteredMG）
		}

	} else { // 接口

		var line int
		var prev *ast.Ident // 以前的“类型”标识符
		for i, f := range list {
			var name *ast.Ident // 名字或零
			if len(f.Names) > 0 {
				name = f.Names[0]
			}
			if i > 0 {
				// TODO（gri）如果类型列表是
				// 过滤或未报告的方法“）
				min := 1
				if prev != nil && name == prev {
					min = 0
				}
				p.linebreak(p.lineFor(f.Pos()), min, ignore, p.linesFrom(line) > 0)
			}
			p.setComment(f.Doc)
			p.recordLine(&line)
			if name != nil {
				p.expr(name)
				prev = nil
			} else {
				p.expr(f.Type)
				prev = nil
			}
			p.setComment(f.Comment)
		}
		if isIncomplete {
			if len(list) > 0 {
				p.print(formfeed)
			}
		}

	}
	p.print(unindent, formfeed, rbrace, token.RBRACE)
}

// ---------------------------------------------------------------
// 表达式

func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
	switch e.Op.Precedence() {
	case 4:
		has4 = true
	case 5:
		has5 = true
	}

	switch l := e.X.(type) {
	case *ast.BinaryExpr:
		if l.Op.Precedence() < e.Op.Precedence() {
			// 将插入参数。
			// 假装这是一个*ast.ParenExpr，什么也不做。
			break
		}
		h4, h5, mp := walkBinary(l)
		has4 = has4 || h4
		has5 = has5 || h5
		if maxProblem < mp {
			maxProblem = mp
		}
	}

	switch r := e.Y.(type) {
	case *ast.BinaryExpr:
		if r.Op.Precedence() <= e.Op.Precedence() {
			// 将插入参数。
			// 假装这是一个*ast.ParenExpr，什么也不做。
			break
		}
		h4, h5, mp := walkBinary(r)
		has4 = has4 || h4
		has5 = has5 || h5
		if maxProblem < mp {
			maxProblem = mp
		}

	case *ast.StarExpr:
			maxProblem = 5
		}

	case *ast.UnaryExpr:
		switch e.Op.String() + r.Op.String() {
		case "/*", "&&", "&^":
			maxProblem = 5
		case "++", "--":
			if maxProblem < 4 {
				maxProblem = 4
			}
		}
	}
	return
}

func cutoff(e *ast.BinaryExpr, depth int) int {
	has4, has5, maxProblem := walkBinary(e)
	if maxProblem > 0 {
		return maxProblem + 1
	}
	if has4 && has5 {
		if depth == 1 {
			return 5
		}
		return 4
	}
	if depth == 1 {
		return 6
	}
	return 4
}

func diffPrec(expr ast.Expr, prec int) int {
	x, ok := expr.(*ast.BinaryExpr)
	if !ok || prec != x.Op.Precedence() {
		return 1
	}
	return 0
}

func reduceDepth(depth int) int {
	depth--
	if depth < 1 {
		depth = 1
	}
	return depth
}

// 格式化二进制表达式：确定截止值，然后格式化。
// 让我们调用depth==1正常模式，depth>1紧凑模式。
// （Russ Cox的算法建议）
// 
// 先例是：
// 5*/%<<>&和&^
// 4+-| ^
// 3==！=<=>=
// 2&&
// 1 | | 
// 
// 唯一的决定是在第4层和第5层周围是否有空格。
// 级别6（一元数）上永远没有空格，级别3及以下总是有空格。
// 
// 要选择截止点，请查看整个表达式，但不包括主
// 表达式（函数调用，括号中的表达式），并应用以下规则：
// 
// 1）如果有一个二元运算符的右侧一元操作数
// 没有空格，截止日期必须是（按顺序）：
// 
// 6 
// &&6 
// &^6 
// +5 
// -5 
// 
// （比较运算符周围总是有空格）
// 
// 2）如果混合了5级和4级运算符，则在正常模式下
// 5（使用空格区分优先级）和在紧凑模式下
// 4（从不使用空格）。
// 
// 3）如果没有4级运算符或5级运算符，则
// 截止值在正常模式下为6（始终使用空格），在紧凑模式下为4（从不使用空格）。
// 
func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int) {
	prec := x.Op.Precedence()
	if prec < prec1 {
		// 需要括号
		// 注意：解析器插入ast。ParenExpr节点；因此，只有以不同的方式创建AST时，才会出现这种情况
		// 。
		p.print(token.LPAREN)
		p.expr0(x, reduceDepth(depth)) // 括号在运算符之前撤消一级深度
		p.print(token.RPAREN)
		return
	}

	printBlank := prec < cutoff

	ws := indent
	p.expr1(x.X, prec, depth+diffPrec(x.X, prec))
	if printBlank {
		p.print(blank)
	}
	xline := p.pos.Line // 它可能在下一行
	yline := p.lineFor(x.Y.Pos())
	p.print(x.OpPos, x.Op)
	if xline != yline && xline > 0 && yline > 0 {
		// 至少有一个断线，但是要尊重一个额外的空行
		// 在源
		if p.linebreak(yline, 1, ws, true) > 0 {
			ws = ignore
			printBlank = false // 换行后没有空格
		}
	}
	if printBlank {
		p.print(blank)
	}
	p.expr1(x.Y, prec+1, depth+1)
	if ws == ignore {
		p.print(unindent)
	}
}

func isBinary(expr ast.Expr) bool {
	_, ok := expr.(*ast.BinaryExpr)
	return ok
}

func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
	p.print(expr.Pos())

	switch x := expr.(type) {
	case *ast.BadExpr:
		p.print("BadExpr")

	case *ast.Ident:
		p.print(x)

	case *ast.BinaryExpr:
		if depth < 1 {
			p.internalError("depth < 1:", depth)
			depth = 1
		}
		p.binaryExpr(x, prec1, cutoff(x, depth), depth)

	case *ast.KeyValueExpr:
		p.expr(x.Key)
		p.print(x.Colon, token.COLON, blank)
		p.expr(x.Value)

	case *ast.StarExpr:
		const prec = token.UnaryPrec
		if prec < prec1 {
			// 需要括号
			p.print(token.LPAREN)
			p.print(token.MUL)
			p.expr(x.X)
			p.print(token.RPAREN)
		} else {
			// 不需要括号
			p.print(token.MUL)
			p.expr(x.X)
		}

	case *ast.UnaryExpr:
		const prec = token.UnaryPrec
		if prec < prec1 {
			// 需要括号
			p.print(token.LPAREN)
			p.expr(x)
			p.print(token.RPAREN)
		} else {
			// 不需要括号
			p.print(x.Op)
			if x.Op == token.RANGE {
				// 如果无法访问，请删除此代码。
				p.print(blank)
			}
			p.expr1(x.X, prec, depth)
		}

	case *ast.BasicLit:
		if p.Config.Mode&normalizeNumbers != 0 {
			x = normalizedNumber(x)
		}
		p.print(x)

	case *ast.FuncLit:
		p.print(x.Type.Pos(), token.FUNC)
		// 请参阅funcDecl中有关如何计算头大小的注释。
		startCol := p.out.Column - len("func")
		p.signature(x.Type)
		p.funcBody(p.distanceFrom(x.Type.Pos(), startCol), blank, x.Body)

	case *ast.ParenExpr:
		if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
			// 不要在已插入括号的表达式周围打印括号
			p.expr0(x.X, depth)
		} else {
			p.print(token.LPAREN)
			p.print(x.Rparen, token.RPAREN)
		}

	case *ast.SelectorExpr:
		p.selectorExpr(x, depth, false)

	case *ast.TypeAssertExpr:
		p.expr1(x.X, token.HighestPrec, depth)
		p.print(token.PERIOD, x.Lparen, token.LPAREN)
		if x.Type != nil {
			p.expr(x.Type)
		} else {
			p.print(token.TYPE)
		}
		p.print(x.Rparen, token.RPAREN)

	case *ast.IndexExpr:
		p.expr1(x.X, token.HighestPrec, 1)
		p.print(x.Lbrack, token.LBRACK)
		p.expr0(x.Index, depth+1)
		p.print(x.Rbrack, token.RBRACK)

	case *ast.IndexListExpr:
		// 一级深度
		p.expr1(x.X, token.HighestPrec, 1)
		p.print(x.Lbrack, token.LBRACK)
		p.exprList(x.Lbrack, x.Indices, depth+1, commaTerm, x.Rbrack, false)
		p.print(x.Rbrack, token.RBRACK)

	case *ast.SliceExpr:
		// TODO（gri）：应该像括号一样对待[]并撤销一级深度
		p.expr1(x.X, token.HighestPrec, 1)
		p.print(x.Lbrack, token.LBRACK)
		indices := []ast.Expr{x.Low, x.High}
		if x.Max != nil {
			indices = append(indices, x.Max)
		}
		// 确定是否需要在“：”
		var needsBlanks bool
		if depth <= 1 {
			var indexCount int
			var hasBinaries bool
			for _, x := range indices {
				if x != nil {
					indexCount++
					if isBinary(x) {
						hasBinaries = true
					}
				}
			}
			if indexCount > 1 && hasBinaries {
				needsBlanks = true
			}
		}
		for i, x := range indices {
			if i > 0 {
				if indices[i-1] != nil && needsBlanks {
					p.print(blank)
				}
				p.print(token.COLON)
				if x != nil && needsBlanks {
					p.print(blank)
				}
			}
			if x != nil {
				p.expr0(x, depth+1)
			}
		}
		p.print(x.Rbrack, token.RBRACK)

	case *ast.CallExpr:
		if len(x.Args) > 1 {
			depth++
		}
		var wasIndented bool
		if _, ok := x.Fun.(*ast.FuncType); ok {
			// 转换为文字函数类型需要在类型
			p.print(token.LPAREN)
			wasIndented = p.possibleSelectorExpr(x.Fun, token.HighestPrec, depth)
			p.print(token.RPAREN)
		} else {
			wasIndented = p.possibleSelectorExpr(x.Fun, token.HighestPrec, depth)
		}
		p.print(x.Lparen, token.LPAREN)
		if x.Ellipsis.IsValid() {
			p.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis, false)
			p.print(x.Ellipsis, token.ELLIPSIS)
			if x.Rparen.IsValid() && p.lineFor(x.Ellipsis) < p.lineFor(x.Rparen) {
				p.print(token.COMMA, formfeed)
			}
		} else {
			p.exprList(x.Lparen, x.Args, depth, commaTerm, x.Rparen, false)
		}
		p.print(x.Rparen, token.RPAREN)
		if wasIndented {
			p.print(unindent)
		}

	case *ast.CompositeLit:
		// 复合文字元素周围加上括号复合文字本身的类型可能会被省略
		if x.Type != nil {
			p.expr1(x.Type, token.HighestPrec, depth)
		}
		p.level++
		p.print(x.Lbrace, token.LBRACE)
		p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace, x.Incomplete)
		// 不要在/*-风格的注释
		// 之后插入额外的换行符，因为如果
		// 没有尾随“}”，它可能会中断代码，“
		mode := noExtraLinebreak
		// 在结束“}”之前，不要在/*风格的注释后面插入额外的空格，除非文本为空
		if len(x.Elts) > 0 {
			mode |= noExtraBlank
		}
		// 需要初始缩进来打印带有
		// 缩进的正确级别
		p.print(indent, unindent, mode, x.Rbrace, token.RBRACE, mode)
		p.level--

	case *ast.Ellipsis:
		p.print(token.ELLIPSIS)
		if x.Elt != nil {
			p.expr(x.Elt)
		}

	case *ast.ArrayType:
		p.print(token.LBRACK)
		if x.Len != nil {
			p.expr(x.Len)
		}
		p.print(token.RBRACK)
		p.expr(x.Elt)

	case *ast.StructType:
		p.print(token.STRUCT)
		p.fieldList(x.Fields, true, x.Incomplete)

	case *ast.FuncType:
		p.print(token.FUNC)
		p.signature(x)

	case *ast.InterfaceType:
		p.print(token.INTERFACE)
		p.fieldList(x.Methods, false, x.Incomplete)

	case *ast.MapType:
		p.print(token.MAP, token.LBRACK)
		p.expr(x.Key)
		p.print(token.RBRACK)
		p.expr(x.Value)

	case *ast.ChanType:
		switch x.Dir {
		case ast.SEND | ast.RECV:
			p.print(token.CHAN)
		case ast.RECV:
			p.print(token.ARROW, token.CHAN) // x。Arrow和x.Pos（）是相同的
		case ast.SEND:
			p.print(token.CHAN, x.Arrow, token.ARROW)
		}
		p.print(blank)
		p.expr(x.Value)

	default:
		panic("unreachable")
	}
}

// normalizedNumber重写基本前缀和指数
// 的数字使用小写字母（0X123到0X123和1.2E3到1.2E3），
// 并从整数假想文字（0765i到765i）中删除前导0。
// 它只保留十六进制数字。
// 
// normalizedNumber不修改ast。BasicLit值指示灯指向。
// 如果lit不是一个数字或已经不是标准格式的数字，
// lit将按原样返回。否则，将出现一个新的ast。BasicLit已创建。
func normalizedNumber(lit *ast.BasicLit) *ast.BasicLit {
	if lit.Kind != token.INT && lit.Kind != token.FLOAT && lit.Kind != token.IMAG {
		return lit // 不是数字-无需操作
	}
	if len(lit.Value) < 2 {
		return lit // 只有一个数字（常见情况）-无需操作
	}
	// len（lit.Value）>=2 

	// 我们忽略lit。好样的，因为是为了光明。种类==代币。IMAG文本可以是整数
	// 或浮点值，十进制或非十进制。相反，只考虑文字模式。
	x := lit.Value
	switch x[:2] {
	default:
		// 0-前缀八进制，十进制整数，或float（可能带有'i'后缀）
		if i := strings.LastIndexByte(x, 'E'); i >= 0 {
			x = x[:i] + "e" + x[i+1:]
			break
		}
		// 从整数（但不是浮点）假想文字中删除前导0 
		if x[len(x)-1] == 'i' && !strings.ContainsAny(x, ".e") {
			x = strings.TrimLeft(x, "0_")
			if x == "i" {
				x = "0i"
			}
		}
	case "0X":
		x = "0x" + x[2:]
		// 可能是十六进制浮点
		if i := strings.LastIndexByte(x, 'P'); i >= 0 {
			x = x[:i] + "p" + x[i+1:]
		}
	case "0x":
		// 可能是十六进制浮点
		i := strings.LastIndexByte(x, 'P')
		if i == -1 {
			return lit // 无需执行
		}
		x = x[:i] + "p" + x[i+1:]
	case "0O":
		x = "0o" + x[2:]
	case "0o":
		return lit // 无需执行
	case "0B":
		x = "0b" + x[2:]
	case "0b":
		return lit // 无需执行
	}

	return &ast.BasicLit{ValuePos: lit.ValuePos, Kind: lit.Kind, Value: x}
}

func (p *printer) possibleSelectorExpr(expr ast.Expr, prec1, depth int) bool {
	if x, ok := expr.(*ast.SelectorExpr); ok {
		return p.selectorExpr(x, depth, true)
	}
	p.expr1(expr, prec1, depth)
	return false
}

// selectorExpr处理*阿斯特。选择orexpr node并报告x是否跨越
// 多行。
func (p *printer) selectorExpr(x *ast.SelectorExpr, depth int, isMethod bool) bool {
	p.expr1(x.X, token.HighestPrec, depth)
	p.print(token.PERIOD)
	if line := p.lineFor(x.Sel.Pos()); p.pos.IsValid() && p.pos.Line < line {
		p.print(indent, newline, x.Sel.Pos(), x.Sel)
		if !isMethod {
			p.print(unindent)
		}
		return true
	}
	p.print(x.Sel.Pos(), x.Sel)
	return false
}

func (p *printer) expr0(x ast.Expr, depth int) {
	p.expr1(x, token.LowestPrec, depth)
}

func (p *printer) expr(x ast.Expr) {
	const depth = 1
	p.expr1(x, token.LowestPrec, depth)
}

// ---------------------------------------------------------------
// 语句

// 打印缩进的语句列表，但在最后一条语句后不带换行符。
// 源代码中语句之间的额外换行符是受尊重的，但语句之间最多打印一行
// 空行。
func (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool) {
	if nindent > 0 {
		p.print(indent)
	}
	var line int
	i := 0
	for _, s := range list {
		// 忽略空语句（was问题3466）
		if _, isEmpty := s.(*ast.EmptyStmt); !isEmpty {
			// 仅对于switch/select case子句列表，nindent==0；
			// 在这种情况下，每个子句都是一个新的部分
			if len(p.output) > 0 {
				// 如果我们不在输出的开头，只打印换行符
				// （即，我们不只是打印一个部分程序）
				p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || p.linesFrom(line) > 0)
			}
			p.recordLine(&line)
			p.stmt(s, nextIsRBrace && i == len(list)-1)
			// 带标签的语句将标签放在单独的一行，但是这里我们只关心实际语句的起始行，不带标签-每个标签的正确行，块打印一个*ast。BlockStmt；它总是跨越至少两条线。
			for t := s; ; {
				lt, _ := t.(*ast.LabeledStmt)
				if lt == nil {
					break
				}
				line++
				t = lt.Stmt
			}
			i++
		}
	}
	if nindent > 0 {
		p.print(unindent)
	}
}

func (p *printer) block(b *ast.BlockStmt, nindent int) {
	p.print(b.Lbrace, token.LBRACE)
	p.stmtList(b.List, nindent, true)
	p.linebreak(p.lineFor(b.Rbrace), 1, ignore, true)
	p.print(b.Rbrace, token.RBRACE)
}

func isTypeName(x ast.Expr) bool {
	switch t := x.(type) {
	case *ast.Ident:
		return true
	case *ast.SelectorExpr:
		return isTypeName(t.X)
	}
	return false
}

func stripParens(x ast.Expr) ast.Expr {
	if px, strip := x.(*ast.ParenExpr); strip {
		// 如果有任何
		// 以
		// 类型名
		ast.Inspect(px.X, func(node ast.Node) bool {
			switch x := node.(type) {
			case *ast.ParenExpr:
				// 括号保护封闭的复合文字
				return false
			case *ast.CompositeLit:
				if isTypeName(x.Type) {
					strip = false // 不要去掉括号
				}
				return false
			}
			// 在所有其他情况下，继续检查
			return true
		})
		if strip {
			return stripParens(px.X)
		}
	}
	return x
}

func stripParensAlways(x ast.Expr) ast.Expr {
	if x, ok := x.(*ast.ParenExpr); ok {
		return stripParensAlways(x.X)
	}
	return x
}

func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) {
	p.print(blank)
	needsBlank := false
	if init == nil && post == nil {
		// 不需要分号
		if expr != nil {
			p.expr(stripParens(expr))
			needsBlank = true
		}
	} else {
		// 所有分号都需要
		// （它们不是分隔符，显式打印它们）
		if init != nil {
			p.stmt(init, false)
		}
		p.print(token.SEMICOLON, blank)
		if expr != nil {
			p.expr(stripParens(expr))
			needsBlank = true
		}
		if isForStmt {
			p.print(token.SEMICOLON, blank)
			needsBlank = false
			if post != nil {
				p.stmt(post, false)
				needsBlank = true
			}
		}
	}
	if needsBlank {
		p.print(blank)
	}
}

// 缩进列表报告如果
// 批量缩进，表达式列表是否会看起来更好（从第一个元素开始，而不是从第一个换行符开始）。
// 
func (p *printer) indentList(list []ast.Expr) bool {
	// 启发式：indentList报告列表中是否有多个多个
	// 行元素，或者是否有任何元素不是
	// 从前一行结束的同一行开始。
	if len(list) >= 2 {
		var b = p.lineFor(list[0].Pos())
		var e = p.lineFor(list[len(list)-1].End())
		if 0 < b && b < e {
			// 列表跨越多行
			n := 0 // 多行元素计数
			line := b
			for _, x := range list {
				xb := p.lineFor(x.Pos())
				xe := p.lineFor(x.End())
				if line < xb {
					// x不是从同一行开始的，而是从上一行结束的，如果中间没有注释，则在换行前应用
					return true
				}
				if xb < xe {
					n++
				}
				line = xe
			}
			return n > 1
		}
	}
	return false
}

func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) {
	p.print(stmt.Pos())

	switch s := stmt.(type) {
	case *ast.BadStmt:
		p.print("BadStmt")

	case *ast.DeclStmt:
		p.decl(s.Decl)

	case *ast.EmptyStmt:

	case *ast.LabeledStmt:
		p.print(unindent)
		p.expr(s.Label)
		p.print(s.Colon, token.COLON, indent)
		if e, isEmpty := s.Stmt.(*ast.EmptyStmt); isEmpty {
			if !nextIsRBrace {
				p.print(newline, e.Pos(), token.SEMICOLON)
				break
			}
		} else {
			p.linebreak(p.lineFor(s.Stmt.Pos()), 1, ignore, true)
		}
		p.stmt(s.Stmt, nextIsRBrace)

	case *ast.ExprStmt:
		const depth = 1
		p.expr0(s.X, depth)

	case *ast.SendStmt:
		const depth = 1
		p.expr0(s.Chan, depth)
		p.print(blank, s.Arrow, token.ARROW, blank)
		p.expr0(s.Value, depth)

	case *ast.IncDecStmt:
		const depth = 1
		p.expr0(s.X, depth+1)
		p.print(s.TokPos, s.Tok)

	case *ast.AssignStmt:
		var depth = 1
		if len(s.Lhs) > 1 && len(s.Rhs) > 1 {
			depth++
		}
		p.exprList(s.Pos(), s.Lhs, depth, 0, s.TokPos, false)
		p.print(blank, s.TokPos, s.Tok, blank)
		p.exprList(s.TokPos, s.Rhs, depth, 0, token.NoPos, false)

	case *ast.GoStmt:
		p.print(token.GO, blank)
		p.expr(s.Call)

	case *ast.DeferStmt:
		p.print(token.DEFER, blank)
		p.expr(s.Call)

	case *ast.ReturnStmt:
		p.print(token.RETURN)
		if s.Results != nil {
			p.print(blank)
			// 使用缩进列表启发式使角落案例看起来更好
			// 第1207期。一种更系统的方法是
			// 始终缩进，但这将导致代码库的重大重新格式化，而不一定会导致更精确的代码格式。
			if p.indentList(s.Results) {
				p.print(indent)
				// 使用NoPos，这样新行就不会出现在
				// 结果之前（见第32854期）。
				p.exprList(token.NoPos, s.Results, 1, noIndent, token.NoPos, false)
				p.print(unindent)
			} else {
				p.exprList(token.NoPos, s.Results, 1, 0, token.NoPos, false)
			}
		}

	case *ast.BranchStmt:
		p.print(s.Tok)
		if s.Label != nil {
			p.print(blank)
			p.expr(s.Label)
		}

	case *ast.BlockStmt:
		p.block(s, 1)

	case *ast.IfStmt:
		p.print(token.IF)
		p.controlClause(false, s.Init, s.Cond, nil)
		p.block(s.Body, 1)
		if s.Else != nil {
			p.print(blank, token.ELSE, blank)
			switch s.Else.(type) {
			case *ast.BlockStmt, *ast.IfStmt:
				p.stmt(s.Else, nextIsRBrace)
			default:
				// 这只能在
				// 构造错误的情况下发生。允许打印，但打印时要确保
				// 可以正确解析。
				p.print(token.LBRACE, indent, formfeed)
				p.stmt(s.Else, true)
				p.print(unindent, formfeed, token.RBRACE)
			}
		}

	case *ast.CaseClause:
		if s.List != nil {
			p.print(token.CASE, blank)
			p.exprList(s.Pos(), s.List, 1, 0, s.Colon, false)
		} else {
			p.print(token.DEFAULT)
		}
		p.print(s.Colon, token.COLON)
		p.stmtList(s.Body, 1, nextIsRBrace)

	case *ast.SwitchStmt:
		p.print(token.SWITCH)
		p.controlClause(false, s.Init, s.Tag, nil)
		p.block(s.Body, 0)

	case *ast.TypeSwitchStmt:
		p.print(token.SWITCH)
		if s.Init != nil {
			p.print(blank)
			p.stmt(s.Init, false)
			p.print(token.SEMICOLON)
		}
		p.print(blank)
		p.stmt(s.Assign, false)
		p.print(blank)
		p.block(s.Body, 0)

	case *ast.CommClause:
		if s.Comm != nil {
			p.print(token.CASE, blank)
			p.stmt(s.Comm, false)
		} else {
			p.print(token.DEFAULT)
		}
		p.print(s.Colon, token.COLON)
		p.stmtList(s.Body, 1, nextIsRBrace)

	case *ast.SelectStmt:
		p.print(token.SELECT, blank)
		body := s.Body
		if len(body.List) == 0 && !p.commentBefore(p.posFor(body.Rbrace)) {
			// 在一行打印空select语句，不带注释
			p.print(body.Lbrace, token.LBRACE, body.Rbrace, token.RBRACE)
		} else {
			p.block(body, 0)
		}

	case *ast.ForStmt:
		p.print(token.FOR)
		p.controlClause(true, s.Init, s.Cond, s.Post)
		p.block(s.Body, 1)

	case *ast.RangeStmt:
		p.print(token.FOR, blank)
		if s.Key != nil {
			p.expr(s.Key)
			if s.Value != nil {
				// 使用逗号后的值位置作为
				// 逗号位置用于正确的注释位置
				p.print(s.Value.Pos(), token.COMMA, blank)
				p.expr(s.Value)
			}
			p.print(blank, s.TokPos, s.Tok, blank)
		}
		p.print(token.RANGE, blank)
		p.expr(stripParens(s.X))
		p.print(blank)
		p.block(s.Body, 1)

	default:
		panic("unreachable")
	}
}

// ---------------------------------------------------------------
// 声明

// keepTypeColumn函数确定是否必须保留一系列
// 连续常量或var声明的类型列，或者是否可以将初始化
// 值（V）放在类型列（T）中。如果必须保留spec[i]中的type列，则结果片中的第i项
// 为真。例如，声明：
// 
// const（
// foobar int=42
// x=7
// foo 
// bar=991 
// ）
// 都没有类型，则可以将一系列值列（V）移动到类型列中（我们只移动整列，以便它们正确对齐）。
// 
// 矩阵格式的结果
// 矩阵
// tV->tV->true存在一个T，因此类型
// /-V-V-true列必须保持
// /--false 
// /-V-false V移到T列
// 
func keepTypeColumn(specs []ast.Spec) []bool {
	m := make([]bool, len(specs))

	populate := func(i, j int, keepType bool) {
		if keepType {
			for ; i < j; i++ {
				m[i] = true
			}
		}
	}

	i0 := -1 // 如果i0>=0，我们正在运行，i0是运行的开始
	var keepType bool
	for i, s := range specs {
		t := s.(*ast.ValueSpec)
		if t.Values != nil {
			if i0 < 0 {
				// 使用非零值
				i0 = i
				keepType = false
			}
		} else {
			if i0 >= 0 {
				// 运行的开始
				populate(i0, i, keepType)
				i0 = -1
			}
		}
		if t.Type != nil {
			keepType = true
		}
	}
	if i0 >= 0 {
		// 运行的结束
		populate(i0, len(specs), keepType)
	}

	return m
}

func (p *printer) valueSpec(s *ast.ValueSpec, keepType bool) {
	p.setComment(s.Doc)
	p.identList(s.Names, false) // 始终显示
	extraTabs := 3
	if s.Type != nil || keepType {
		p.print(vtab)
		extraTabs--
	}
	if s.Type != nil {
		p.expr(s.Type)
	}
	if s.Values != nil {
		p.print(vtab, token.ASSIGN, blank)
		p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos, false)
		extraTabs--
	}
	if s.Comment != nil {
		for ; extraTabs > 0; extraTabs-- {
			p.print(vtab)
		}
		p.setComment(s.Comment)
	}
}

func sanitizeImportPath(lit *ast.BasicLit) *ast.BasicLit {
	// 注意：go/parser生成的未修改的AST
	// 包含反向或双引号路径字符串，该字符串不包含任何无效字符，并且大部分工作
	// 此处不需要。但是，修改或生成的AST 
	// 可能包含非规范路径。在
	// /所有情况下都要做这项工作，因为它不太难，也不要求速度。

	// 如果我们没有正确的字符串，请保持保守，并返回我们拥有的任何内容
	if lit.Kind != token.STRING {
		return lit
	}
	s, err := strconv.Unquote(lit.Value)
	if err != nil {
		return lit
	}

	// 如果字符串是无效路径，返回我们所有的
	// 
	// spec:“实现限制：编译器可以将
	// importPath限制为非空字符串，只使用属于Unicode的L、M、N、P和s常规类别的
	// 字符（图形
	// 无空格字符）也可能不包括
	// /！“#$%&'（）*，：；<=>？[\]^`{124;}和Unicode替换字符
	// U+FFFD。”
	if s == "" {
		return lit
	}
	const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
	for _, r := range s {
		if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
			return lit
		}
	}

	// 否则，返回双引号路径
	s = strconv.Quote(s)
	if s == lit.Value {
		return lit // 照明没有问题
	}
	return &ast.BasicLit{ValuePos: lit.ValuePos, Kind: token.STRING, Value: s}
}

// 参数n是组中的规格数。如果设置了doIndent，当遇到第一个
// 换行符时，规范中的多行标识符列表将缩进。
// 
func (p *printer) spec(spec ast.Spec, n int, doIndent bool) {
	switch s := spec.(type) {
	case *ast.ImportSpec:
		p.setComment(s.Doc)
		if s.Name != nil {
			p.expr(s.Name)
			p.print(blank)
		}
		p.expr(sanitizeImportPath(s.Path))
		p.setComment(s.Comment)
		p.print(s.EndPos)

	case *ast.ValueSpec:
		if n != 1 {
			p.internalError("expected n = 1; got", n)
		}
		p.setComment(s.Doc)
		p.identList(s.Names, doIndent) // 始终显示
		if s.Type != nil {
			p.print(blank)
			p.expr(s.Type)
		}
		if s.Values != nil {
			p.print(blank, token.ASSIGN, blank)
			p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos, false)
		}
		p.setComment(s.Comment)

	case *ast.TypeSpec:
		p.setComment(s.Doc)
		p.expr(s.Name)
		if s.TypeParams != nil {
			p.parameters(s.TypeParams, typeTParam)
		}
		if n == 1 {
			p.print(blank)
		} else {
			p.print(vtab)
		}
		if s.Assign.IsValid() {
			p.print(token.ASSIGN, blank)
		}
		p.expr(s.Type)
		p.setComment(s.Comment)

	default:
		panic("unreachable")
	}
}

func (p *printer) genDecl(d *ast.GenDecl) {
	p.setComment(d.Doc)
	p.print(d.Pos(), d.Tok, blank)

	if d.Lparen.IsValid() || len(d.Specs) > 1 {
		// 一组带括号的声明
		p.print(d.Lparen, token.LPAREN)
		if n := len(d.Specs); n > 0 {
			p.print(indent, formfeed)
			if n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR) {
				// 两个或多个分组的常量/变量声明：
				// 确定是否必须保留类型列
				keepType := keepTypeColumn(d.Specs)
				var line int
				for i, s := range d.Specs {
					if i > 0 {
						p.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0)
					}
					p.recordLine(&line)
					p.valueSpec(s.(*ast.ValueSpec), keepType[i])
				}
			} else {
				var line int
				for i, s := range d.Specs {
					if i > 0 {
						p.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0)
					}
					p.recordLine(&line)
					p.spec(s, n, false)
				}
			}
			p.print(unindent, formfeed)
		}
		p.print(d.Rparen, token.RPAREN)

	} else if len(d.Specs) > 0 {
		// 单个声明
		p.spec(d.Specs[0], 1, true)
	}
}

// 节点大小确定格式化后的字符大小。
// 如果节点与at 
// 大多数maxSize字符和格式化输出不包含
// 任何控制字符。否则，结果是>maxSize。
// 
func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
	// nodeSize调用打印机，打印机可能会递归调用nodeSize 
	// 。对于深层复合文字嵌套，这可能导致指数算法。还记得之前的
	// 删除递归的结果吗（was问题1628）。
	if size, found := p.nodeSizes[n]; found {
		return size
	}

	size = maxSize + 1 // 假设n不适合
	p.nodeSizes[n] = size

	// 节点大小计算必须独立于特定的
	// 样式，以便我们总是得到相同的决定；打印
	// RAW格式
	cfg := Config{Mode: RawFormat}
	var buf bytes.Buffer
	if err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil {
		return
	}
	if buf.Len() <= maxSize {
		for _, ch := range buf.Bytes() {
			if ch < ' ' {
				return
			}
		}
		size = buf.Len() // n fits 
		p.nodeSizes[n] = size
	}
	return
}

// numLines返回原始源中节点n跨越的行数。
func (p *printer) numLines(n ast.Node) int {
	if from := n.Pos(); from.IsValid() {
		if to := n.End(); to.IsValid() {
			return p.lineFor(to) - p.lineFor(from) + 1
		}
	}
	return infinity
}

// bodySize类似于nodeSize，但它专门用于*ast。BlockStmt's.
func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int {
	pos1 := b.Pos()
	pos2 := b.Rbrace
	if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) {
		// 打开和关闭大括号在不同的行上-不要将其设置为一行
		return maxSize + 1
	}
	if len(b.List) > 5 {
		// 语句太多-不要将其设置为一行
		return maxSize + 1
	}
	// 否则，估算正文大小
	bodySize := p.commentSizeBefore(p.posFor(pos2))
	for i, s := range b.List {
		if bodySize > maxSize {
			break // 无需继续
		}
		if i > 0 {
			bodySize += 2 // 分号空格和空白
		}
		bodySize += p.nodeSize(s, maxSize)
	}
	return bodySize
}

// funcBody在给定headerSize的函数头后面打印函数正文。
// 如果标题和块的大小“足够小”，并且块“足够简单”，则
// 块打印在当前行上，没有换行符，在9月之前与标题隔开
// 否则块的开头“{”打印在当前行上，后面是
// 行用于块的语句和结束“}”。
// 
func (p *printer) funcBody(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
	if b == nil {
		return
	}

	// 保存/恢复复合文字嵌套级别
	defer func(level int) {
		p.level = level
	}(p.level)
	p.level = 0

	const maxSize = 100
	if headerSize+p.bodySize(b, maxSize) <= maxSize {
		p.print(sep, b.Lbrace, token.LBRACE)
		if len(b.List) > 0 {
			p.print(blank)
			for i, s := range b.List {
				if i > 0 {
					p.print(token.SEMICOLON, blank)
				}
				p.stmt(s, i == len(b.List)-1)
			}
			p.print(blank)
		}
		p.print(noExtraLinebreak, b.Rbrace, token.RBRACE, noExtraLinebreak)
		return
	}

	if sep != ignore {
		p.print(blank) // 始终使用空白
	}
	p.block(b, 1)
}

// distanceFrom返回p.out（当前输出
// 位置）和startOutCol之间的列差。如果起始位置与
// 当前位置（或其中一个位置未知）在不同的行上，则结果为无穷大。
func (p *printer) distanceFrom(startPos token.Pos, startOutCol int) int {
	if startPos.IsValid() && p.pos.IsValid() && p.posFor(startPos).Line == p.pos.Line {
		return p.out.Column - startOutCol
	}
	return infinity
}

func (p *printer) funcDecl(d *ast.FuncDecl) {
	p.setComment(d.Doc)
	p.print(d.Pos(), token.FUNC, blank)
	// 我们只有在发出FUNC后才能保存startCol；否则，它可以位于
	// 不同的行上（只有在发出
	// FUNC时，才会发出FUNC前面的所有空格）。
	startCol := p.out.Column - len("func ")
	if d.Recv != nil {
		p.parameters(d.Recv, funcParam) // 方法：打印接收器
		p.print(blank)
	}
	p.expr(d.Name)
	p.signature(d.Type)
	p.funcBody(p.distanceFrom(d.Pos(), startCol), vtab, d.Body)
}

func (p *printer) decl(decl ast.Decl) {
	switch d := decl.(type) {
	case *ast.BadDecl:
		p.print(d.Pos(), "BadDecl")
	case *ast.GenDecl:
		p.genDecl(d)
	case *ast.FuncDecl:
		p.funcDecl(d)
	default:
		panic("unreachable")
	}
}

// ----------------------------------------------------------------------------------------
// 文件

func declToken(decl ast.Decl) (tok token.Token) {
	tok = token.ILLEGAL
	switch d := decl.(type) {
	case *ast.GenDecl:
		tok = d.Tok
	case *ast.FuncDecl:
		tok = token.FUNC
	}
	return
}

func (p *printer) declList(list []ast.Decl) {
	tok := token.ILLEGAL
	for _, d := range list {
		prev := tok
		tok = declToken(d)
		// 如果声明标记更改（例如，从CONST更改为TYPE）
		// 或者下一个声明有与其相关的文档，
		// 在顶级声明之间打印一条空行。
		// （因为调用p.linebreak时使用了d的位置，而
		// 超过了任何文档，所以满足了最低要求
		// 即使没有额外的getDoc（d）nil检查，也可以保留它，以防
		// linebreak逻辑得到改进——已经有一个TODO了）。
		if len(p.output) > 0 {
			// 如果我们不在输出的开头，只打印换行符
			// （即，我们不只是打印一个部分程序）
			min := 1
			if prev != tok || getDoc(d) != nil {
				min = 2
			}
			// 如果下一个声明是一个跨越多行的函数
			// 则启动一个新的部分
			p.linebreak(p.lineFor(d.Pos()), min, ignore, tok == token.FUNC && p.numLines(d) > 1)
		}
		p.decl(d)
	}
}

func (p *printer) file(src *ast.File) {
	p.setComment(src.Doc)
	p.print(src.Pos(), token.PACKAGE, blank)
	p.expr(src.Name)
	p.declList(src.Decls)
	p.print(newline)
}
